require 'test/helper'

#LectroTest-like Property objects
class TC_Property < Test::Unit::TestCase

  # Creation tests, the Property is never checked
  def test_creation_one
    # we don't want source filters and we have nice reflection in ruby,
    # thus small interface change:
    #  Property.new('description',*Generators, &code)
    # Values are passed in order  to &code

    desc="property description"
    prop=Property.new(desc,Int) { function_to_execute }
    ok prop
    assert_equal desc, prop.description
    assert_equal 1, prop.generators.size
  end

  def test_creation_many
    ok Property.new("sum of 2 int is an int", Int, Int) { not run }
    prop = Property.new("sum of 4 int is an int", Int, Int, Int) {not run }
    ok prop
    assert_equal 3, prop.generators.size
  end

  def test_creation_float
    # XXX floats offer a bunch of special cases such as returning Infinity
    # from some operations
    # they are useful to test but keep this in mind :)
    ok Property.new('something with floats', Float) { not run }
  end

  def test_creation_char
    # a Char generator may be useful but a Char class does not exist in ruby
    # What other generators may be useful?
    ok Property.new("each char has a numeric value", Char) { not run }
  end

  def test_check_property_fails
    prop = Property.new("int is always > 0", Int) {|i| false}
    # XXX mii che nome orrendo
    assert_raises(PropertyBrokenException) {prop.check}
  end
  
  def test_check_property_fails_real
    # this makes the test repeatible. 
    
    srand(140880)
    prop = Property.new("int is always > 0", Int) {|i| i > 0}
    # XXX mii che nome orrendo
    assert_raises(PropertyBrokenException) {prop.check}
  end

  def test_failed_property_format
    srand(0.796316260982166)

    prop = Property.new("int is always > 0", Int) {|i| i > 0}
    prop.check
    #if this line is reached we have failed
    fail
  rescue PropertyBrokenException => e
    assert_kind_of Test::Unit::AssertionFailedError, e, 
                  "if test/unit is loaded before t/q PBE is a subclass of AFE"
    assert_equal [-830161448], e.breaking_values
    assert_equal %{Property "int is always > 0" broken in 15 attempts with values: [-830161448]},
                  e.message

  end

  def test_failing
    # this test must fails because it tests that a property broken is reported correctly
    Property.new(<<Eof, Int) { false }.check

This test must fail because it is needed to verify the output in test/unit.
It should be reported as a Failure (not Error), and that File and Line are correct.
This message is the property description and thus is reported as part of the test/unit output.
The number of attempts must be 0 and the random value, well, random.
Eof
    
  end

  def test_failed_property_format_with_multiple_values
    srand(0)

    prop = Property.new("X is always < 2Y", Int, Int) {|i,j| i < 2*j}
    prop.check
    #if this line is reached we have failed
    fail
  rescue PropertyBrokenException => e
    assert_equal [1700352277, 577165042], e.breaking_values
    assert_equal %{Property "X is always < 2Y" broken in 5 attempts with values: [1700352277, 577165042]},
                 e.message

  end

  def test_check_property_holds
    prop = Property.new("an int is an int", Int) {|x| x.is_a?(Int) }
    prop.check
    ok true
  end

  def test_prop_check_returns
    # prop must return something. Number of attempts to prove the thing false,
    # seems a good idea. Default: 500
    prop = Property.new("int * int => int",Int,Int) {|a,b| (a * b).is_a?(Int)}
    num_attempts = 500
    assert_equal num_attempts, prop.check
  end

  def test_prop_check_max
    # but we want to change that
    prop = Property.new("int",Int) {|a| true}
    assert_equal 10,  prop.check(10)
    assert_equal 501, prop.check(501)
  end

end
